package de.jmda.core.mproc;

import static de.jmda.core.mproc.ProcessingUtilities.normalizeBinaryClassName;
import static de.jmda.core.mproc.ProcessingUtilities.withEnclosedTypeElements;
import static de.jmda.core.util.CollectionsUtil.asSet;
import static org.junit.Assert.assertEquals;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.lang.model.element.TypeElement;

import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;

import de.jmda.core.mproc.RelationAnalyser;
import de.jmda.core.mproc.RelationAnalyser.RelationInfoHas;
import de.jmda.core.mproc.task.AbstractTypeElementsTaskTypes;
import de.jmda.core.mproc.task.TaskException;
import de.jmda.core.mproc.task.TaskRunner;

@SuppressWarnings("unused")
public class JUTRelationAnalyser
{
	private final static Logger LOGGER =
      Logger.getLogger(JUTRelationAnalyser.class);

	private class CHasOneFieldSource
	{
		private CHasAFieldTarget hasOneFieldTarget;
	}

	private class CHasManyFieldArraySource
	{
		private CHasAFieldTarget[] hasManyFieldArrayTargets;
	}

	private class CHasManyFieldCollectionSource
	{
		private Collection<CHasAFieldTarget> hasManyFieldCollectionTargets;
		private List<CHasAFieldTarget> hasManyFieldListTargets;
		private ArrayList<CHasAFieldTarget> hasManyFieldArrayListTargets;
	}

	private class CHasManyFieldArrayListSource
	{
		private ArrayList<CHasAFieldTarget> hasManyFieldCollectionTargets;
	}

	private class CHasAFieldTarget	{	}
	
	private interface IHasOneFieldSource
	{
		IHasAFieldTarget hasOneFieldTarget = null;
	}
	
	private interface IHasManyFieldArraySource
	{
		IHasAFieldTarget[] hasManyFieldArrayTargets = null;
	}

	private interface IHasManyFieldCollectionSource
	{
		Collection<IHasAFieldTarget> hasManyFieldCollectionTargets = null;
		List<IHasAFieldTarget> hasManyFieldListTargets = null;
		ArrayList<IHasAFieldTarget> hasManyFieldArrayListTargets = null;
	}

	private interface IHasManyFieldArrayListSource
	{
		ArrayList<IHasAFieldTarget> hasManyFieldCollectionTargets = null;
	}

	private interface IHasAFieldTarget	{	}
	
	private class Task extends AbstractTypeElementsTaskTypes
	{
		private Map<String, TypeElement> typeElementsByName = new HashMap<>();

		private Task(Set<? extends Class<?>> types)
		{
			super(types);
		}

		@Override
		public boolean execute() throws TaskException
		{
			for (TypeElement type : withEnclosedTypeElements(getTypeElements()))
			{
				typeElementsByName.put(type.asType().toString(), type);
			}
			
			return false;
		}
	}

	private Task task;

	@Before
	public void before() throws IOException
	{
		task = new Task(asSet(JUTRelationAnalyser.class));
		TaskRunner.run(task);
	}

	@Test
	public void testRelationInfos()
	{
		for (TypeElement sourceTypeElement : task.typeElementsByName.values())
		{
			for (TypeElement targetTypeElement : task.typeElementsByName.values())
			{
				RelationInfoHas[] relationInfosHas =
						RelationAnalyser.calculateRelationInfoHas(
								sourceTypeElement, targetTypeElement);
				
				for (RelationInfoHas relationInfoHas : relationInfosHas)
				{
					LOGGER.debug("found has relation: " + relationInfoHas);
				}
			}
		}
	}

	@Test
	public void testHasOneFieldSourceWithClass()
	{
		TypeElement source =
				task.typeElementsByName
				    .get(normalizeBinaryClassName(CHasOneFieldSource.class));
		TypeElement target =
				task.typeElementsByName
				    .get(normalizeBinaryClassName(CHasAFieldTarget.class));
		
		RelationInfoHas[] relationInfosHas =
				RelationAnalyser.calculateRelationInfoHas(source, target);
		
		assertEquals(
				"unexpected number of has relations", 1, relationInfosHas.length);
	}

	@Test
	public void testHasOneFieldSourceWithInterface()
	{
		TypeElement source =
				task.typeElementsByName
				    .get(normalizeBinaryClassName(IHasOneFieldSource.class));
		TypeElement target =
				task.typeElementsByName
				    .get(normalizeBinaryClassName(IHasAFieldTarget.class));
		
		RelationInfoHas[] relationInfosHas =
				RelationAnalyser.calculateRelationInfoHas(source, target);
		
		assertEquals(
				"unexpected number of has relations", 1, relationInfosHas.length);
	}

	@Test
	public void testHasManyFieldCollectionSourceWithClass()
	{
		TypeElement source =
				task.typeElementsByName
				    .get(normalizeBinaryClassName(CHasManyFieldCollectionSource.class));
		TypeElement target =
				task.typeElementsByName
				    .get(normalizeBinaryClassName(CHasAFieldTarget.class));

		RelationInfoHas[] relationInfosHas =
				RelationAnalyser.calculateRelationInfoHas(source, target);

		assertEquals(
				"unexpected number of has relations", 3, relationInfosHas.length);
	}

	@Test
	public void testHasManyFieldCollectionSourceWithInterface()
	{
		TypeElement source =
				task.typeElementsByName
				    .get(normalizeBinaryClassName(IHasManyFieldCollectionSource.class));
		TypeElement target =
				task.typeElementsByName
				    .get(normalizeBinaryClassName(IHasAFieldTarget.class));
		
		RelationInfoHas[] relationInfosHas =
				RelationAnalyser.calculateRelationInfoHas(source, target);

		assertEquals(
				"unexpected number of has relations", 3, relationInfosHas.length);
	}

	@Test
	public void testHasManyFieldArrayListSourceWithClass()
	{
		TypeElement source =
				task.typeElementsByName
				    .get(normalizeBinaryClassName(CHasManyFieldArrayListSource.class));
		TypeElement target =
				task.typeElementsByName
				    .get(normalizeBinaryClassName(CHasAFieldTarget.class));
		
		RelationInfoHas[] relationInfosHas =
				RelationAnalyser.calculateRelationInfoHas(source, target);

		assertEquals(
				"unexpected number of has relations", 1, relationInfosHas.length);
	}

	@Test
	public void testHasManyFieldArrayListSourceWithInterface()
	{
		TypeElement source =
				task.typeElementsByName
				    .get(normalizeBinaryClassName(IHasManyFieldArrayListSource.class));
		TypeElement target =
				task.typeElementsByName
				    .get(normalizeBinaryClassName(IHasAFieldTarget.class));
		
		RelationInfoHas[] relationInfosHas =
				RelationAnalyser.calculateRelationInfoHas(source, target);

		assertEquals(
				"unexpected number of has relations", 1, relationInfosHas.length);
	}
}